package club.jdiy.admin.controller;

import club.jdiy.admin.interceptor.GuestDisabled;
import club.jdiy.admin.util.IdUtil;
import club.jdiy.core.AdminContext;
import club.jdiy.core.base.JDiyCtrl;
import club.jdiy.core.base.JDiyService;
import club.jdiy.core.base.Java8MyDateEditor;
import club.jdiy.core.base.domain.DBEntity;
import club.jdiy.core.base.domain.Pager;
import club.jdiy.core.base.domain.Removable;
import club.jdiy.core.base.domain.Ret;
import club.jdiy.core.ex.JDiyException;
import club.jdiy.core.storage.Storable;
import club.jdiy.utils.poi.ExcelUtils;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 通用Controller代码基础类.<br>
 * 注意：此类仅使用于后台Controller. 请勿在前台Controller类中继承此类!!
 *
 * @param <T>        Entity实体类名
 * @param <TService> 实体Service类名
 */
@SuppressWarnings("ALL")
public abstract class JDiyAdminCtrl<T extends DBEntity, TService extends JDiyService> extends JDiyCtrl<T, TService> {

    public JDiyAdminCtrl() {
        tClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        className = tClass.getName().substring(tClass.getName().lastIndexOf(".") + 1);
        modelName = className.substring(0, 1).toLowerCase() + className.substring(1);
    }

    @RequestMapping(value = "/ls")
    public String ls(T qo,
                     String orderField,
                     String orderDirection,
                     @RequestParam(defaultValue = "15") Integer pageSize,
                     Integer page,
                     ModelMap map,
                     HttpServletRequest request) {
        if (pageSize == null || pageSize < 0) pageSize = 15;
        if (page == null || page < 1) page = 1;

        Pager<T> pager = service.findPager(pageSize, page, qo, orderField, orderDirection);
        if (qo instanceof Storable) {
            for (T it : pager.getItems()) ((Storable) it).initStore(context);
        }
        map.put("modelName", modelName);
        map.put("qo", qo);
        map.put("pager", pager);
        map.put("orderField", orderField);
        map.put("orderDirection", orderDirection);
        return String.format("mgmt/%s/ls.ftl", modelName);
    }


    @RequestMapping("in")
    public String in(String id, ModelMap map, HttpServletRequest request) throws Exception {
        Object vo;
        if (id == null || "".equals(id) || "0".equals(id)) {
            vo = tClass.getDeclaredConstructor().newInstance();
            Class<?> rType = tClass.getMethod("getId").getReturnType();
            if (String.class.isAssignableFrom(rType)) {

                tClass.getMethod("setId", rType).invoke(vo, "0");
            } else if (Integer.class.isAssignableFrom(rType) || int.class.isAssignableFrom(rType)) {
                tClass.getMethod("setId", rType).invoke(vo, 0);
            } else if (Long.class.isAssignableFrom(rType) || long.class.isAssignableFrom(rType)) {
                tClass.getMethod("setId", rType).invoke(vo, 0L);
            }
        } else {
            Class<?> rType = tClass.getMethod("getId").getReturnType();
            if ((Long.class.isAssignableFrom(rType) || long.class.isAssignableFrom(rType)))
                vo = service.findById(Long.parseLong(id)).orElse(null);
            else if ((Integer.class.isAssignableFrom(rType) || int.class.isAssignableFrom(rType)))
                vo = service.findById(Integer.parseInt(id)).orElse(null);
            else
                vo = service.findById(id).orElse(null);
        }
        if (vo instanceof Storable) {
            ((Storable) vo).initStore(context);
        }
        map.put("vo", vo);
        map.put("modelName", modelName);
        return String.format("mgmt/%s/in.ftl", modelName);
    }

    @GuestDisabled
    @RequestMapping("save")
    @ResponseBody
    public Ret<?> save(T vo, HttpServletRequest request) throws Exception {
        Method gm = tClass.getMethod("getId");
        Class<?> rType = gm.getReturnType();
        Object id = gm.invoke(vo);
        if ("0".equals(id) && String.class.isAssignableFrom(rType)) {
            tClass.getMethod("setId", rType).invoke(vo, IdUtil.newId());
        }
        try {
            service.logicSave(vo);
            return Ret.success();
        } catch (JDiyException le) {
            return Ret.error(le);
        }
    }

    @GuestDisabled
    @RequestMapping("remove")
    @ResponseBody
    public Ret<?> remove(String[] id, HttpServletRequest request) {
        try {
            Class rType = tClass.getMethod("getId").getReturnType();
            Method setMethod = tClass.getMethod("setId", rType);
            for (String _id : id) {
                if (_id == null || "".equals(_id)) continue;
                T vo = tClass.getDeclaredConstructor().newInstance();
                if (rType == Long.class || rType == long.class) setMethod.invoke(vo, Long.parseLong(_id));
                else if (rType == Integer.class || rType == int.class) setMethod.invoke(vo, Integer.parseInt(_id));
                else setMethod.invoke(vo, _id);
                if (vo instanceof Storable && !(vo instanceof Removable)) {
                    ((Storable) vo).initStore(context).getStore().delAll();
                }
                service.delete(vo);
            }
            return Ret.success();
        } catch (JDiyException le) {
            return Ret.fail(le.getMessage());
        } catch (Exception le) {
            le.printStackTrace();
            return Ret.fail("对不起，删除失败，可能此条目已被系统业务使用。");
        }
    }

    protected void export(ExcelUtils et, String xlsName, int flushCache, T qo,
                          String orderField,
                          String orderDirection, HttpServletRequest request,
                          HttpServletResponse response) throws Exception {
        Pager<T> pager = service.findPager(flushCache > 0 ? flushCache : 300, 1, qo, orderField, orderDirection);
        while (pager != null && pager.getPage() <= pager.getPageCount()) {
            et.addData(pager.getItems());
            if (pager.getPage() < pager.getPageCount()) {
                pager = service.findPager(flushCache > 0 ? flushCache : 300, pager.getPage() + 1, qo, orderField, orderDirection);
            } else pager = null;
        }
        et.output(request, response, xlsName);
    }

    //public abstract void setService(TService service);


    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDateTime.class, new Java8MyDateEditor(Java8MyDateEditor.Type.LocalDateTime));
        binder.registerCustomEditor(LocalDate.class, new Java8MyDateEditor(Java8MyDateEditor.Type.LocalDate));
    }

    @Resource
    protected AdminContext context;
    protected Class<T> tClass;
    protected String className;
    protected String modelName;
}
